<?php

declare(strict_types=1);

namespace Zyucv7\Core\ORM;

use Zyucv7\Core\Database\Driver as DatabaseDriver;
use Zyucv7\Core\ORM\Pager;

/**
 * 查询构造器
 */
class Query
{
    /** @var string 表名 */
    private string $table;

    /** @var array 查询项 */
    private $options = [
        'columns' => '*',
        'joins' => [],
        'orders' => [],
        'groups' => []
    ];

    /** @var array 对应查询的参数值 */
    private $values = [];

    /** @var DatabaseDriver 数据库对象 */
    private $db;

    public function __construct(string $table, DatabaseDriver $db)
    {
        $this->table = $table;
        $this->db = $db;
    }

    /**
     * 设置查询字段
     * @param array|string $columns
     * @return $this
     */
    public function select($columns)
    {
        if (is_array($columns)) {
            $this->options['columns'] = implode(', ', $columns);
        } else if (is_string($columns)) {
            $this->options['columns'] = $columns;
        }
        return $this;
    }

    /**
     * 添加WHERE条件
     * @param string $column
     * @param mixed $operator
     * @param mixed $value
     * @return $this
     */
    public function where(string $column, $operator, $value)
    {
        if (!isset($this->options['and_where'])) {
            $this->options['and_where'] = [];
        }
        $this->options['and_where'][] = $column . ' ' . $operator . ' ?';
        $this->values[] = $value;
        return $this;
    }

    /**
     * 添加WHERE条件 OR
     * @param string $column
     * @param mixed $operator
     * @param mixed $value
     * @return $this
     */
    public function orWhere(string $column, $operator, $value)
    {
        if (!isset($this->options['or_where'])) {
            $this->options['or_where'] = [];
        }
        $this->options['or_where'][] = $column . ' ' . $operator . ' ?';
        $this->values[] = $value;
        return $this;
    }

    /**
     * 通过语句的方式WHERE条件，传入类型，和参数
     * @param string $sql
     * @param array $value
     * @param mixed $type 类型 = OR , AND，默认=AND
     * @return $this
     */
    public function whereBySql(string $sql, array $values, $type = 'AND')
    {
        if ($type == 'AND') {
            if (!isset($this->options['and_where'])) {
                $this->options['and_where'] = [];
            }
            $this->options['and_where'][] = $sql;
        } else {
            if (!isset($this->options['or_where'])) {
                $this->options['or_where'] = [];
            }
            $this->options['or_where'][] = $sql;
        }

        $this->values = array_merge($this->values, $values);
    }

    /**
     * 添加原生的WHERE条件
     * @param string $sql
     * @return $this
     */
    public function whereRaw(string $sql, array $values = [])
    {
        unset($this->options['and_where']);
        unset($this->options['or_where']);
        $this->options['raw_where'] = $sql;
        $this->values = $values;
        return $this;
    }

    /**
     * 添加排序条件
     * @param string $column
     * @param string $direction
     * @return $this
     */
    public function order(string $column, string $direction = 'ASC')
    {
        $this->options['orders'][] = $column . ' ' . $direction;
        return $this;
    }

    /**
     * 添加分组条件
     * @param string $column
     * @param string $direction
     * @return $this
     */
    public function group(string $column)
    {
        $this->options['groups'][] = $column;
        return $this;
    }

    /**
     * 设置结果数量限制
     * @param int $limit
     * @param int $offset
     * @return $this
     */
    public function limit(int $limit, int $offset = -1)
    {
        if ($limit > 0) {
            if ($offset >= 0) {
                $this->options['limit'] = $offset . ', ' . $limit;
            } else {
                $this->options['limit'] = $limit;
            }
        }
        return $this;
    }

    /**
     * 添加JOIN条件
     * @param string $join
     * @return $this
     */
    public function join(string $join)
    {
        $this->options['joins'][] = $join;
        return $this;
    }

    /**
     * 转换options对象，成为数据库执行的SQL对象
     */
    private function toSqlParams()
    {
        $options = $this->options;
        $queryOptions = [];
        $queryOptions['order'] = isset($options['orders']) ? implode(', ', $options['orders']) : '';
        $queryOptions['limit'] = $options['limit'] ?? '';
        $queryOptions['fields'] = $options['columns'] ?? '*';
        if (isset($options['raw_where'])) {
            $queryOptions['where'] = $options['raw_where'];
        } else {
            $where_arr = [];
            if (isset($options['and_where'])) {
                $where_arr[] = '(' . implode(' AND ', $options['and_where']) . ')';
            }
            if (isset($options['or_where'])) {
                $where_arr[] = '(' . implode(' OR ', $options['or_where']) . ')';
            }
            if (count($where_arr) > 0) {
                $queryOptions['where'] = trim(implode(' AND ', $where_arr));
            }
        }
        if (isset($options['joins'])) {
            $queryOptions['join'] = implode(' ', $options['joins']);
        }
        if (isset($options['groups'])) {
            $queryOptions['group'] = implode(', ', $options['groups']);
        }

        // 返回索引数组以便使用 list($queryOptions, $values) 解构
        return [$queryOptions, $this->values];
    }

    /**
     * 查找单个记录
     * @return mixed
     */
    public function first()
    {
        list($queryOptions, $values) = $this->toSqlParams();
        $where = $queryOptions['where'] ?? '';
        return $this->db->findOne($this->table, $queryOptions, $values);
    }

    /**
     * 获取记录总数
     * @return int
     */
    public function count()
    {
        list($queryOptions, $values) = $this->toSqlParams();
        $where = $queryOptions['where'] ?? '';
        // 使用驱动的 findValue 获取单个聚合值
        $val = $this->db->findValue($this->table, "COUNT(0) as count", $where, $values);
        return intval($val ?? 0);
    }

    /**
     * 获得数据集合
     * @return mixed
     */
    public function getAll()
    {
        list($queryOptions, $values) = $this->toSqlParams();
        return $this->db->findAll($this->table, $queryOptions, $values);
    }

    /**
     * 查找单个值
     * @return mixed
     */
    public function getVal($colum, $where, $params = [])
    {
        return $this->db->findValue($this->table, $colum, $where, $params);
    }

    /**
     * 分页
     */
    public function paginate($page, $perPage = 10)
    {
        $fields = $this->options['columns']; // 先把原始的字段名记录下来

        $this->options['columns'] = "COUNT(0) as count";
        $item = $this->first();
        $count = 0;
        if ($item) {
            $count = intval($item['count']);
        }
        $pager = new Pager($count, $page, $perPage);
        $pagerInfo = $pager->getPaginationInfo();
        // 重置查询字段
        $this->options['columns'] = $fields;
        $this->limit($perPage, $pager->getOffset());
        $pagerInfo['rows'] = $this->getAll();
        return $pagerInfo;
    }

    public function resetValues()
    {
        $this->values = [];
        return $this;
    }

    public function setValues(array $values = [])
    {
        $this->values = array_merge($this->values, $values);
        return $this;
    }
}
